package cn.jbolt.wechat.service;

import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.util.StrUtil;
import cn.jbolt.core.api.JBoltApiKit;
import cn.jbolt.core.api.JBoltApiRet;
import cn.jbolt.core.base.JBoltMsg;
import cn.jbolt.core.common.enums.JBoltSystemLogTargetType;
import cn.jbolt.core.db.sql.Sql;
import cn.jbolt.core.model.Application;
import cn.jbolt.core.model.User;
import cn.jbolt.core.para.JBoltPara;
import cn.jbolt.core.service.JBoltUserService;
import cn.jbolt.core.service.base.JBoltBaseService;
import cn.jbolt.core.util.JBoltArrayUtil;
import cn.jbolt.core.util.JBoltRandomUtil;
import cn.jbolt.wechat.cache.JBoltWechatConfigCache;
import cn.jbolt.wechat.cache.JBoltWechatUserCache;
import cn.jbolt.wechat.common.WechatApiResult;
import cn.jbolt.wechat.common.WechatMpinfoType;
import cn.jbolt.wechat.common.mp.WechatMpContext;
import cn.jbolt.wechat.model.WechatMpinfo;
import cn.jbolt.wechat.model.WechatUser;
import com.jfinal.aop.Inject;
import com.jfinal.kit.Kv;
import com.jfinal.kit.Okv;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Page;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.bean.result.WxMpUserList;

import java.sql.Struct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 微信用户信息_模板表
 *
 * @ClassName: WechatUserService
 * @author: 山东小木
 * @date: 2023-10-13 16:15
 */
public class JBoltWechatUserService extends JBoltBaseService<WechatUser> {
    private final WechatUser dao = new WechatUser().dao();

    @Inject
    private JBoltWechatMpinfoService wechatMpinfoService;
    @Inject
    private JBoltUserService userService;
    /**
     * 同步状态
     */
    private static final ConcurrentHashMap<Long, Boolean> SYNCING_MAP = new ConcurrentHashMap<>();

    @Override
    protected WechatUser dao() {
        return dao;
    }

    @Override
    protected int systemLogTargetType() {
        return JBoltSystemLogTargetType.NONE.getValue();
    }

    /**
     * 后台管理数据查询
     *
     * @param mpId       公众号id
     * @param pageNumber 第几页
     * @param pageSize   每页几条数据
     * @param keywords   关键词
     * @param sortColumn 排序列名
     * @param sortType   排序方式 asc desc
     * @param subscribe  是否已关注
     * @param isChecked  是否已验证
     * @param enable     禁用访问
     * @return
     */
    public Page<WechatUser> getAdminDatas(Long mpId, int pageNumber, int pageSize, String keywords,Integer sex, String sortColumn, String sortType, Boolean subscribe, Boolean isChecked, Boolean enable) {
        //创建sql对象
        Sql sql = selectSql().eq(WechatUser.MP_ID, mpId).page(pageNumber, pageSize);
        sql.eq("sex", sex);
        //sql条件处理
        sql.eqBooleanToChar("subscribe", subscribe);
        sql.eqBooleanToChar("is_checked", isChecked);
        sql.eqBooleanToChar("enable", enable);
        //关键词模糊查询
        sql.likeMulti(keywords, "nickname", "remark", "real_name");
        //排序
        sql.orderBy(sortColumn, sortType);
        return paginate(sql);
    }


    /**
     * 根据ID获得一条数据转为WechatUser model
     *
     * @param mpId 公众号id
     * @param id
     * @return
     */
    public WechatUser findByIdToWechatUser(Long mpId, Object id) {
        return findFirst(selectSql().eq(WechatUser.MP_ID, mpId).eq("id", id));
    }

    /**
     * 根据openId获取用户 api专用
     *
     * @param mpId
     * @param openId
     * @return
     */
    public WechatUser getWechatUserByOpenId(Long mpId, String openId) {
        if (hasNotOk(mpId, openId)) return null;
        return findFirst(selectSql()
                .select("id,real_name,nickname,open_id,union_id,sex,subscribe,head_img_url,enable,session_key,mp_id,bind_system_user,phone,weixin,last_login_time")
                .eq(WechatUser.MP_ID, mpId)
                .eq(WechatUser.OPEN_ID, openId));
    }

    /**
     * 根据unionId获取用户 api专用
     *
     * @param mpId
     * @param unionId
     * @return
     */
    public WechatUser getApiWechatUserByUnionId(Long mpId, String unionId) {
        if (hasNotOk(mpId, unionId)) {
            return null;
        }

        return findFirst(selectSql()
                .select(
                        "id,real_name,nickname,open_id,union_id,union_id,sex,subscribe,head_img_url,enable,session_key,mp_id,bind_system_user,phone,weixin,last_login_time")
                .eq(WechatUser.MP_ID, mpId)
                .eq(WechatUser.UNION_ID, unionId));

    }

    /**
     * 公众号用户授权登录 添加一个未关注的用户信息
     *
     * @param mpId
     * @param openId
     * @param userInfo
     * @return
     */
    public Ret addUnSubscribeWechatUserInfo(Long mpId, String openId, WxOAuth2UserInfo userInfo) {
        Date now = new Date();
        WechatUser wechatUser = new WechatUser();
        wechatUser.setSubscribe(false);
        wechatUser.setOpenId(openId);
        wechatUser.setMpId(Long.parseLong(mpId.toString()));
        wechatUser.setEnable(true);
        wechatUser.setFirstAuthTime(now);
        wechatUser.setLastAuthTime(now);
        wechatUser.setCreateTime(now);
        wechatUser.setUpdateTime(now);
        wechatUser.setNickname(userInfo.getNickname());
        wechatUser.setCountry(userInfo.getCountry());
        wechatUser.setProvince(userInfo.getProvince());
        wechatUser.setCity(userInfo.getCity());
        wechatUser.setUnionId(userInfo.getUnionId());
        wechatUser.setHeadImgUrl(userInfo.getHeadImgUrl());

//       todo 新版接口未提供 wechatUser.setLanguage(userInfo);
//        wechatUser.setSex(userInfo.get);
        boolean success = wechatUser.save();
        if (!success) {
            return Ret.fail(JBoltMsg.FAIL);
        }
        //添加日志
        //addSaveSystemLog(wechatUser.getId(), JBoltUserKit.getUserId(), wechatUser.getName());
        return success(wechatUser, JBoltMsg.SUCCESS);

    }

    /**
     * 公众号用户授权登录 添加一个未关注的用户信息
     *
     * @param mpId
     * @param wechatUserId
     * @param userInfo
     * @return
     */
    public Ret updateSubscribeWechatUserInfo(Long mpId, Object wechatUserId, WxOAuth2UserInfo userInfo) {
        Date now = new Date();
        WechatUser wechatUser = findByIdToWechatUser(mpId, wechatUserId);
        if (notOk(wechatUser.getFirstAuthTime())) {
            wechatUser.setFirstAuthTime(now);
        }
        wechatUser.setLastAuthTime(now);
        wechatUser.setUpdateTime(now);
        wechatUser.setNickname(userInfo.getNickname());
        wechatUser.setCountry(userInfo.getCountry());
        wechatUser.setProvince(userInfo.getProvince());
        wechatUser.setCity(userInfo.getCity());
        wechatUser.setUnionId(userInfo.getUnionId());
        wechatUser.setHeadImgUrl(userInfo.getHeadImgUrl());
//        wechatUser.setLanguage(userInfo.getStr("language"));
//        wechatUser.setSex(userInfo.getInt("sex"));
        boolean success = wechatUser.update();
        if (!success) {
            return Ret.fail(JBoltMsg.FAIL);
        }
        //添加日志
        //addSaveSystemLog(wechatUser.getId(), JBoltUserKit.getUserId(), wechatUser.getName());
        return success(wechatUser, JBoltMsg.SUCCESS);
    }

    /**
     * 解绑
     *
     * @param application
     * @param mpId
     * @param wechatUser
     * @param type
     * @param userId
     * @return
     */
    public JBoltApiRet unbindOtherUser(Application application, Long mpId, WechatUser wechatUser, Integer type, Object userId) {
        if (type == WechatUser.TYPE_SYSTEM_USER) {
            if (notOk(wechatUser.getBindSystemUser())) {
                return JBoltApiRet.API_SUCCESS("已解除绑定");
            }
            wechatUser.setBindSystemUser(null);
        } else {
            if (notOk(wechatUser.getBindOther())) {
                return JBoltApiRet.API_SUCCESS("已解除绑定");
            }
            String unBindUser = type + "_" + userId.toString();
            if (!checkExistBindUserInfo(wechatUser.getBindOther(), unBindUser)) {
                return JBoltApiRet.API_SUCCESS;
            }

            String bindUser = wechatUser.getBindOther().replace(unBindUser, "");
            if (bindUser.startsWith(",")) {
                bindUser = bindUser.substring(1);
            }
            if (bindUser.endsWith(",")) {
                bindUser = bindUser.substring(0, bindUser.length() - 1);
            }
            wechatUser.setBindOther(bindUser);


        }
        Ret ret = update(wechatUser.getMpId(), wechatUser);

        if (ret.isFail()) {
            return JBoltApiRet.API_FAIL(application, "解绑失败");
        }
        return JBoltApiRet.API_SUCCESS("解绑成功");
    }


    /**
     * 微信用户绑定其他用户
     *
     * @param application
     * @param mpId
     * @param wechatUser
     * @param type
     * @param userName
     * @param password
     * @return
     */
    public JBoltApiRet bindOtherUser(Application application, Long mpId, WechatUser wechatUser, Integer type, String userName, String password) {
        JBoltApiRet ret = null;
        switch (type.intValue()) {
            case WechatUser.TYPE_SYSTEM_USER:
                ret = bindSystemUser(application, wechatUser, userName, password);
                break;
        }
        if (ret == null) {
            ret = JBoltApiRet.WECHAT_XCX_BINDUSER_ERROR(application);
        }
        return ret;
    }


    /**
     * 绑定系统用户
     *
     * @param application
     * @param wechatUser
     * @param userName
     * @param password
     * @return
     */
    private JBoltApiRet bindSystemUser(Application application, WechatUser wechatUser, String userName, String password) {
        Ret ret = userService.getUser(userName, password);
        User user = ret.isFail() ? null : ret.getAs("data");
        if (user == null) {
            return JBoltApiRet.WECHAT_XCX_BINDUSER_NO_USER(application, "系统用户");
        }
        if (user.getEnable() == null || !user.getEnable()) {
            return JBoltApiRet.API_FAIL("绑定用户已被禁用");
        }
        if (notOk(wechatUser.getBindSystemUser())) {
            //如果没有 就直接赋值
            wechatUser.setBindSystemUser(user.getId());
            ret = update(wechatUser.getMpId(), wechatUser);
            if (ret.isFail()) {
                return JBoltApiRet.WECHAT_XCX_BINDUSER_UPDATE_ERROR(application, "系统用户");
            }
            return JBoltApiRet.API_SUCCESS;
        } else {
            //如果相等或者已经绑定 直接success就好了
            if (wechatUser.getBindSystemUser().equals(user.getId())) {
                return JBoltApiRet.API_SUCCESS;
            }
            //判断是不是已经存在这个类型的用户绑定 同一个类型下
            return JBoltApiRet.WECHAT_XCX_BINDUSER_SAMETYPE_EXIST(application, "系统用户");

        }


    }


    /**
     * 微信小程序手机号授权拿到用户解密数据后更新数据库
     *
     * @param mpId
     * @param wechatAppId
     * @param userId
     * @param phoneInfo
     * @return
     */
    public Ret updateWxaPhoneNumberByDecrypt(Long mpId, String wechatAppId, Object userId, WxMaPhoneNumberInfo phoneInfo) {
        String waterMarkAppId = phoneInfo.getWatermark().getAppid();
        //判断水印
        if (waterMarkAppId == null) {
            return fail(String.format("微信小程序用户手机号信息更新时,未获取到appId[%s-%s]", mpId, userId));
        }
        //判断水印里的APPID
        if (wechatAppId.equals(waterMarkAppId) == false) {
            return fail(String.format("微信小程序用户手机号信息更新时,appId不匹配[%s-%s]", mpId, userId));
        }
        //判断微信用户
        WechatUser wechatUser = findByIdToWechatUser(mpId, userId);
        if (wechatUser == null) {
            return fail(String.format("微信小程序用户手机号信息更新时,找不到指定的用户信息数据[%s-%s]", mpId, userId));
        }

        wechatUser.setPhone(phoneInfo.getPurePhoneNumber());
        wechatUser.setPhoneCountryCode(phoneInfo.getCountryCode());
        Date now = new Date();
        wechatUser.setUpdateTime(now);
        if (notOk(wechatUser.getFirstAuthTime())) {
            //如果没设置首次注册授权时间 就设置一下
            wechatUser.setFirstAuthTime(now);
            wechatUser.setLastAuthTime(now);
        }
        Ret ret = update(mpId, wechatUser);
        if (ret.isOk()) {
            if (StrKit.notBlank(wechatUser.getOpenId())) {
                //清掉缓存
                clearWechatUserCache(mpId, wechatUser.getId(), wechatUser.getOpenId(), wechatUser.getUnionId());
            }
        }
        return ret.isOk() ? successWithData(Okv.by("phoneNumber", wechatUser.getPhone()).set("countryCode", wechatUser.getPhoneCountryCode())) : fail(String.format("更新微信小程序用户手机号授权信息失败[%s:%s]", mpId, wechatUser.getId()));
    }


    /**
     * 微信小程序授权拿到用户解密数据后更新数据库
     *
     * @param mpId
     * @param userId
     * @param userInfo
     * @return
     */
    public Ret updateWxaUserInfoByDecrypt(Long mpId, Object userId, WxMaUserInfo userInfo) {
        WechatUser wechatUser = findByIdToWechatUser(mpId, userId);
        if (wechatUser == null) {
            return fail(String.format("微信小程序用户信息更新时,找不到指定的用户信息数据[%s-%s]", mpId, userId));
        }
//		String openId=userInfoResult.getStr("openId");
//		if(openId.equals(wechatUser.getOpenId())==false) {
//			return fail(String.format("微信小程序用户信息更新时,openId不一致[%s-%s]", mpId,userId));
//		}
        String oldOpenId = wechatUser.getOpenId();
        String oldUnionId = wechatUser.getUnionId();

        String unionId = userInfo.getUnionId();
        if (StrKit.notBlank(unionId)) {
            wechatUser.setUnionId(unionId);
        }

//		wechatUser.setOpenId(openId);
        wechatUser.setNickname(userInfo.getNickName());
        wechatUser.setSex(userInfo.getGender() == null ? null : Integer.parseInt(userInfo.getGender()));
        wechatUser.setCountry(userInfo.getCountry());
        wechatUser.setProvince(userInfo.getProvince());
        wechatUser.setCity(userInfo.getCity());
        wechatUser.setHeadImgUrl(userInfo.getAvatarUrl());
        wechatUser.setLanguage(userInfo.getLanguage());
        Date now = new Date();
        wechatUser.setUpdateTime(now);
        if (notOk(wechatUser.getFirstAuthTime())) {
            //如果没设置首次注册授权时间 就设置一下
            wechatUser.setFirstAuthTime(now);
            wechatUser.setLastAuthTime(now);
        }
        Ret ret = update(mpId, wechatUser);
        if (ret.isOk()) {
            if (StrKit.notBlank(oldOpenId)) {
                //清掉缓存
                clearWechatUserCache(mpId, wechatUser.getId(), oldOpenId, oldUnionId);
            }
        }
        return ret.isOk() ? SUCCESS : fail(String.format("更新微信小程序用户授权信息失败[%s:%s]", mpId, wechatUser.getId()));
    }


    /**
     * 微信小程序wx.login
     *
     * @param mpId
     * @param openId
     * @param unionId
     * @param sessionKey
     * @return
     */
    public WechatUser saveWechatMaUser(Long mpId, String openId, String unionId, String sessionKey) {
        Date now = new Date();
        WechatUser wechatUser = new WechatUser();
        wechatUser.setEnable(true);
        wechatUser.setIsChecked(false);
        wechatUser.setOpenId(openId);
        wechatUser.setSource(WechatMpinfoType.XCX.getValue());
        wechatUser.setMpId(mpId);
        wechatUser.setUnionId(unionId);
        wechatUser.setSessionKey(sessionKey);
        wechatUser.setNickname("用户_" + JBoltRandomUtil.randomLowWithNumber(6));
        wechatUser.setSubscribe(true);
        wechatUser.setSubscribeTime(now);
        wechatUser.setFirstLoginTime(now);
        wechatUser.setLastLoginTime(now);
        Ret ret = save(mpId, wechatUser);
        if (ret.isFail()) {
            return null;
        }
        return wechatUser;
    }

    /**
     * 更新用户wx小程序登录信息
     *
     * @param mpId
     * @param user
     * @param openId
     * @param unionId
     * @param sessionKey
     * @return
     */
    public Ret updateWechatMaUserLoginInfo(Long mpId, WechatUser user, String openId, String unionId,
                                           String sessionKey) {
        String oldOpenId = user.getOpenId();
        String oldUnionId = user.getUnionId();
        user.setOpenId(openId);
        user.setUnionId(unionId);
        user.setSessionKey(sessionKey);
        Date now = new Date();
        user.setUpdateTime(now);
        user.setLastLoginTime(now);
        Ret ret = update(mpId, user);
        if (ret.isOk()) {
            if (StrKit.notBlank(oldOpenId)) {
                //清掉缓存
                clearWechatUserCache(mpId, user.getId(), oldOpenId, oldUnionId);
            }
        }
        return ret.isOk() ? SUCCESS : fail(String.format("更新微信小程序用户登录信息失败[%s:%s]", mpId, user.getId()));
    }

    /**
     * 更新微信用户信息
     *
     * @param mpId
     * @param para
     * @param isWxa
     * @return
     */
    public Ret updateMyWechatUserInfo(Long mpId, JBoltPara para, boolean isWxa) {
        if (para == null || para.isEmpty()) {
            return fail(JBoltMsg.PARAM_ERROR);
        }
        if (!para.containsKey(ID) || notOk(para.getLong(ID))) {
            return fail("更新用户的数据未指定id");
        }
        Long myWechatUserId = JBoltApiKit.getApiUserIdToLong();
        WechatUser dbUser = JBoltWechatUserCache.me.getApiWechatUserByApiUserId(mpId, myWechatUserId);
        if (dbUser == null) {
            return fail("更新用户的数据不存在");
        }
        if (dbUser.getEnable() == false) {
            return fail("账号已禁用，无法更新");
        }
        WechatUser wechatUser = new WechatUser();
        wechatUser.setId(myWechatUserId);
        boolean needUpdate = false;
        if (para.containsKey("nickname")) {
            wechatUser.setNickname(para.getString("nickname"));
            needUpdate = true;
        }
        if (para.containsKey("headImgUrl")) {
            wechatUser.setHeadImgUrl(para.getString("headImgUrl"));
            needUpdate = true;
        }
        if (para.containsKey("sex")) {
            wechatUser.setSex(para.getInteger("sex"));
            needUpdate = true;
        }
        if (para.containsKey("realname")) {
            wechatUser.setRealName(para.getString("realname"));
            needUpdate = true;
        }
        if (para.containsKey("phone")) {
            wechatUser.setPhone(para.getString("phone"));
            needUpdate = true;
        }
        if (para.containsKey("weixin")) {
            wechatUser.setWeixin(para.getString("wexin"));
            needUpdate = true;
        }
        if (!needUpdate) {
            return fail("参数异常，未设置有效更新属性值");
        }
        wechatUser.setUpdateTime(new Date());
        Ret ret = update(mpId, wechatUser);
        if (ret.isOk()) {
            clearWechatUserCache(mpId, myWechatUserId, wechatUser.getOpenId(), wechatUser.getUnionId());
        }
        return ret;
    }

    public Ret updateUserInfo(String appId, String openId, String column, Object value) {
        Long mpId = JBoltWechatConfigCache.me.getMpidByAppId(appId);
        if (notOk(mpId)) {
            return fail("appId为：" + appId + "的公众号信息获取失败");
        }
        WechatMpinfo wechatMpinfo = wechatMpinfoService.findById(mpId);
        if (wechatMpinfo == null) {
            return fail("微信公众平台信息不存在");
        }
        //根据openId去找人 找到就更新 找不到就新增
        WechatUser wechatUser = getWechatUserByOpenId(mpId, openId);
        if (wechatUser == null) {
            return fail(StrUtil.format("用户[appId:{},openId:{}]不存在", appId, openId));
        }
        updateColumn(wechatUser.getId(), column, value);

        wechatUser.clearCache();
        return SUCCESS;


    }

    /**
     * 同步关注者的用户信息到微信User表
     *
     * @param appId
     * @param openId
     * @return
     */
    public Ret syncSubscribeUserInfo(String appId, String openId) {
        Long mpId = JBoltWechatConfigCache.me.getMpidByAppId(appId);
        if (notOk(mpId)) {
            return fail("appId为：" + appId + "的公众号信息获取失败");
        }
        WechatMpinfo wechatMpinfo = wechatMpinfoService.findById(mpId);
        if (wechatMpinfo == null) {
            return fail("微信公众平台信息不存在");
        }

        //根据openId去找人 找到就更新 找不到就新增
        WechatUser wechatUser = getWechatUserByOpenId(mpId, openId);
        if (wechatUser != null) {
            //说明之前用户信息已经同步到数据库里保存了 这次更新
            Ret ret = processUserInfo(wechatUser);
            if (ret.isOk()) {
                update(mpId, wechatUser);
            }
        } else {
            //说明是个全新用户，存起来
            saveOneNewWechatUserInfo(mpId, wechatMpinfo.getType(), openId);
        }
        return SUCCESS;

    }

    /**
     * 保存一个新的关注用户信息
     *
     * @param mpId
     * @param mpType
     * @param openId
     */
    private void saveOneNewWechatUserInfo(Long mpId, int mpType, String openId) {
        WechatUser wechatUser = genRecordByOpenId(mpId, mpType, openId);
        Ret ret = save(mpId, wechatUser);
        if (ret.isOk()) {
            Ret processRet = processUserInfo(wechatUser);
            if (processRet.isOk()) {
                update(mpId, wechatUser);
            }
        }

    }


    /**
     * 清理缓存
     *
     * @param mpId
     * @param id
     * @param openId
     * @param unionId
     */
    public void clearWechatUserCache(Long mpId, Long id, String openId, String unionId) {
        JBoltWechatUserCache.me.removeApiWechatUser(mpId, id);
        JBoltWechatUserCache.me.removeApiWechatUserByMpOpenId(mpId, openId);
        JBoltWechatUserCache.me.removeApiWechatUserByMpUnionId(mpId, unionId);
    }


    /**
     * 检测是否存在相同数据
     *
     * @param bindUser
     * @param newBindUser
     * @return
     */
    private boolean checkExistBindUserInfo(String bindUser, String newBindUser) {
        if (newBindUser.equals(bindUser)) {
            return true;
        }//相等
        if (bindUser.indexOf(",") == -1) {
            return false;
        }
        String[] arr = JBoltArrayUtil.from(bindUser, ",");
        return JBoltArrayUtil.contains(arr, newBindUser);
    }


    /**
     * 同步用户数据
     *
     * @param mpId
     * @return
     */
    public Ret sync(Long mpId) {
        Ret checkRet = checkCanSync(mpId);
        if (checkRet.isFail()) {
            return checkRet;
        }
        Kv kv = checkRet.getAs("data");
        String appId = kv.getStr("appId");
        WechatMpinfo wechatMpinfo = kv.getAs("wechatMpinfo");
        try {
            WechatMpContext.switchTo(appId);
            Ret ret = syncByNextOpenId(mpId, wechatMpinfo.getType(), null);
            if (ret.isFail()) {
                return ret;
            }
            if (wechatMpinfo.getIsAuthenticated() && wechatMpinfo.getType().intValue() != WechatMpinfoType.XCX.getValue() && wechatMpinfo.getType().intValue() != WechatMpinfoType.QYWX.getValue()) {
                return syncUserInfo(mpId, wechatMpinfo.getType());
            }
            return SUCCESS;
        } finally {
            WechatMpContext.switchOver();
            SYNCING_MAP.remove(mpId);
        }
    }

    /**
     * 检测是否可以同步
     *
     * @param mpId
     * @return
     */
    private Ret checkCanSync(Long mpId) {
        if (notOk(mpId)) {
            return fail(JBoltMsg.PARAM_ERROR);
        }
        if (SYNCING_MAP.putIfAbsent(mpId, true) != null) {
            return fail("已经在同步了，请耐心等待...");
        }
        WechatMpinfo wechatMpinfo = wechatMpinfoService.findById(mpId);
        if (wechatMpinfo == null) {
            return fail("微信公众平台信息不存在");
        }

        String appId = JBoltWechatConfigCache.me.getAppId(mpId);
        if (StrKit.isBlank(appId)) {
            return fail(wechatMpinfo.getName() + "基础配置不正确!");
        }
        boolean canSync = (wechatMpinfo.getIsAuthenticated() && wechatMpinfo.getType().intValue() != WechatMpinfoType.XCX.getValue() && wechatMpinfo.getType().intValue() != WechatMpinfoType.QYWX.getValue());
        if (!canSync) {
            return fail("此公众平台无调用API权限");
        }
        return success(Kv.by("appId", appId).set("wechatMpinfo", wechatMpinfo), JBoltMsg.SUCCESS);
    }


    /**
     * 更新同步
     *
     * @param mpId
     * @param mpType
     * @param nextOpenId
     * @return
     */
    private Ret syncByNextOpenId(Long mpId, Integer mpType, String nextOpenId) {
        WechatApiResult wechatApiResult = null;
        WechatApiResult<WxMpUserList> res = WechatMpContext.getFollowUserList(nextOpenId);
        if (res.isSuccess() == false) {
            return fail(res.getMsg());
        }
        WxMpUserList userList = res.getData();
        Long total = userList.getTotal();
        if (notOk(total)) {
            return fail("这个公众平台用户总数为0");
        }
        Integer count = userList.getCount();
        nextOpenId = userList.getNextOpenid();
        if (notOk(count) || notOk(nextOpenId)) {
            return success("同步完成");
        }
        List<String> openIds = userList.getOpenids();
        List<WechatUser> users = new ArrayList<WechatUser>();
        for (String openId : openIds) {
            boolean exist = exists(selectSql().eq(WechatUser.OPEN_ID, openId).eq(WechatUser.MP_ID, mpId));
            if (exist) {
                continue;
            }
            users.add(genRecordByOpenId(mpId, mpType, openId));
        }
        batchSave(users);
        //递归调用
        return syncByNextOpenId(mpId, mpType, nextOpenId);
    }

    /**
     * 保存一个不重复的OPENID 进入数据库
     *
     * @param mpId
     * @param mpType
     * @param openId
     */
    private WechatUser genRecordByOpenId(Long mpId, Integer mpType, String openId) {
        WechatUser wechatUser = new WechatUser();
        wechatUser.setEnable(true);
        wechatUser.setIsChecked(false);
        wechatUser.setOpenId(openId);
        wechatUser.setSubscribe(true);
        wechatUser.setSource(mpType);
        wechatUser.setMpId(mpId);
        wechatUser.autoProcessIdValue();
        return wechatUser;
    }


    /**
     * 更新用户信息
     *
     * @param mpId
     * @param type
     * @return
     */
    private Ret syncUserInfo(Long mpId, Integer type) {
        List<WechatUser> needSyncUsers = getNeedSyncUsers(mpId, 100);
        if (needSyncUsers == null || needSyncUsers.size() == 0) {
            return success("同步用户信息完成");
        }
        Ret ret;
        for (WechatUser user : needSyncUsers) {
            ret = processUserInfo(user);
            if (ret.isFail()) {
                return ret;
            }
        }
        //批量更新
        batchUpdate(needSyncUsers);
        return syncUserInfo(mpId, type);
    }

    /**
     * 调用接口 去获取用户信息
     *
     * @param user
     */
    private Ret processUserInfo(WechatUser user) {
        WechatApiResult<WxMpUser> res = WechatMpContext.getBaseUserInfo(user.getOpenId());
        if (res.isSuccess() == false) {
            return fail(res.getMsg());
        }
        WxMpUser userInfo = res.getData();
        if (Objects.equals(userInfo.getSubscribe(), true)) {
            String userNickName = user.getNickname();
            //判断如果传进来的user 没有nickname或者有但是是之前自动生成的那种 需要再次设置一下
            if (notOk(userNickName) || (userNickName.indexOf("用户_") != -1 && userNickName.equals("用户_") == false)) {
                String nickName = userInfo.getNickname();
//				if(EmojiUtil.containsEmoji(nickName)) {
//					nickName=EmojiUtil.toHtml(nickName);
//				}else {
//					nickName=JBoltStringUtil.filterEmoji(nickName);
//				}
                if (StrKit.isBlank(nickName)) {
                    nickName = "用户_" + user.get("id");
                }
                user.setNickname(nickName);
            }
            user.set("language", userInfo.getLanguage());
//            微信接口不支持提供这些信息了 user.set("country", userInfo.get);
//            user.set("province", userInfo.get);
//            user.set("city", userInfo.getci);
//            user.set("sex", userInfo.get);
            user.set("union_id", userInfo.getUnionId());
            user.set("remark", userInfo.getRemark());
            user.set("group_id", userInfo.getGroupId());
            user.set("subscribe_scene", userInfo.getSubscribeScene());
            user.set("qr_scene", userInfo.getQrScene());
            user.set("qr_scene_str", userInfo.getQrSceneStr());
            user.set("head_img_url", userInfo.getHeadImgUrl());
            Long subscribeTime = userInfo.getSubscribeTime();
            user.set("subscribe_time", new Date(subscribeTime * 1000L));
        }

        return SUCCESS;
    }


    /**
     * 获取需要同步的数据
     *
     * @param mpId
     * @param count
     * @return
     */
    private List<WechatUser> getNeedSyncUsers(Long mpId, int count) {
        Sql sql = selectSql().select("id", "open_id").isNull("nickname").eq(WechatUser.MP_ID, mpId).firstPage(count);
        return find(sql.toSql());
    }

    /**
     * 同步一个用户数据
     *
     * @param mpId
     * @param id
     * @return
     */
    public Ret syncOneUserInfo(Long mpId, Long id) {
        Ret checkRet = checkCanSync(mpId);
        if (checkRet.isFail()) {
            return checkRet;
        }
        WechatUser user = findFirst(selectSql().eq(WechatUser.MP_ID, mpId).eq(WechatUser.ID, id));
        if (user == null) {
            return fail(JBoltMsg.DATA_NOT_EXIST);
        }
        Kv kv = checkRet.getAs("data");
        String appId = kv.getStr("appId");
        WechatMpContext.switchTo(appId);
        try {
            Ret syncUserInfo = processUserInfo(user);
            if (syncUserInfo.isFail()) {
                return syncUserInfo;
            }
            Ret updateRet = update(mpId, user);
            return updateRet;
        } finally {
            WechatMpContext.switchOver();
            SYNCING_MAP.remove(mpId);
        }
    }


    /**
     * 保存
     *
     * @param wechatUser
     * @return
     */
    public Ret save(Long mpId, WechatUser wechatUser) {
        if (hasNotOk(mpId, wechatUser) || isOk(wechatUser.getId())) {
            return fail(JBoltMsg.PARAM_ERROR);
        }
        wechatUser.setMpId(mpId);
        boolean success = wechatUser.save();
        if (success) {
            //添加日志
            //addSaveSystemLog(wechatUser.getId(), JBoltUserKit.getUserId(), wechatUser.getName());
        }
        return ret(success);
    }

    /**
     * 更新
     *
     * @param mpId
     * @param wechatUser
     * @return
     */
    public Ret update(Long mpId, WechatUser wechatUser) {
        if (hasNotOk(mpId, wechatUser, wechatUser.getId())) {
            return fail(JBoltMsg.PARAM_ERROR);
        }
        //更新时需要判断数据存在
        WechatUser dbWechatUser = findById(wechatUser.getId());
        if (dbWechatUser == null) {
            return fail(JBoltMsg.DATA_NOT_EXIST);
        }
        if (Objects.equals(dbWechatUser.getMpId(), mpId) == false) {
            return fail(JBoltMsg.NOPERMISSION);
        }
        boolean success = wechatUser.update();
        if (success) {
            //添加日志
            //addUpdateSystemLog(wechatUser.getId(), JBoltUserKit.getUserId(), wechatUser.getName());
        }
        return ret(success);
    }


    public Ret delete(Long mpId, Long wechatUserId) {
        if (hasNotOk(mpId, wechatUserId)) {
            return fail(JBoltMsg.PARAM_ERROR);
        }
        //更新时需要判断数据存在
        WechatUser dbWechatUser = findById(wechatUserId);
        if (dbWechatUser == null) {
            return fail(JBoltMsg.DATA_NOT_EXIST);
        }
        if (Objects.equals(dbWechatUser.getMpId(), mpId) == false) {
            return fail(JBoltMsg.NOPERMISSION);
        }
        dbWechatUser.delete();

        return SUCCESS;
    }


    public Ret toggleEnable(Long mpId, Long wechatUserId) {
        if (hasNotOk(mpId, wechatUserId)) {
            return fail(JBoltMsg.PARAM_ERROR);
        }
        //更新时需要判断数据存在
        WechatUser dbWechatUser = findById(wechatUserId);
        if (dbWechatUser == null) {
            return fail(JBoltMsg.DATA_NOT_EXIST);
        }
        if (Objects.equals(dbWechatUser.getMpId(), mpId) == false) {
            return fail(JBoltMsg.NOPERMISSION);
        }
        Ret ret = toggleEnable(wechatUserId);
        return ret;
    }


    /**
     * 关键词查询指定返回个数的数据 默认返回所有字段
     *
     * @param keywords     关键词
     * @param limitCount   返回个数
     * @param always       当关键词空的时候 是否需要查询所有数据返回指定个数
     * @param matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
     * @return
     */
    public List<WechatUser> getAutocompleteList(Long mpId, String keywords, Integer limitCount, Boolean always, String matchColumns) {
        if (notOk(mpId)) {
            return Collections.emptyList();
        }
        return getAutocompleteList(keywords, limitCount, always, null, matchColumns, selectSql().eq(WechatUser.MP_ID, mpId));
    }


    /**
     * 删除数据后执行的回调
     *
     * @param wechatUser 要删除的model
     * @param kv         携带额外参数一般用不上
     * @return
     */
    @Override
    protected String afterDelete(WechatUser wechatUser, Kv kv) {
        //addDeleteSystemLog(wechatUser.getId(), JBoltUserKit.getUserId(),wechatUser.getName());
        return null;
    }

    /**
     * 检测是否可以删除
     *
     * @param wechatUser model
     * @param kv         携带额外参数一般用不上
     * @return
     */
    @Override
    public String checkInUse(WechatUser wechatUser, Kv kv) {
        //这里用来覆盖 检测是否被其它表引用
        return null;
    }

    /**
     * toggle操作执行后的回调处理
     */
    @Override
    protected String afterToggleBoolean(WechatUser wechatUser, String column, Kv kv) {
        //addUpdateSystemLog(wechatUser.getId(), JBoltUserKit.getUserId(), wechatUser.getName(),"的字段["+column+"]值:"+wechatUser.get(column));
        /**
         switch(column){
         case "subscribe":
         break;
         case "is_checked":
         break;
         case "enable":
         break;
         }
         */
        return null;
    }


}
